1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 12 /** 13 * Asset representation of a texture 14 */ 15 module hip.assets.texture; 16 import hip.error.handler; 17 import hip.assets.texture; 18 import hip.math.rect; 19 import hip.assets.image; 20 public import hip.util.data_structures:Array2D; 21 public import hip.api.renderer.texture; 22 public import hip.api.data.image; 23 public import hip.api.graphics.color; 24 public import hip.api.renderer.core: HipResourceUsage; 25 26 class HipTexture : HipAsset 27 { 28 IImage img; 29 // int width,height; 30 private __gshared HipTexture pixelTexture; 31 32 bool hasSuccessfullyLoaded(){return img.getWidth > 0;} 33 public static HipTexture getPixelTexture() 34 { 35 if(pixelTexture is null) 36 { 37 pixelTexture = new HipTexture(HipResourceUsage.Immutable); 38 pixelTexture.img = cast(IImage)Image.getPixelImage(); 39 pixelTexture.textureImpl.load(pixelTexture.img); 40 } 41 return pixelTexture; 42 } 43 44 /** 45 * Make it available for implementors 46 */ 47 IHipTexture textureImpl; 48 /** 49 * Initializes with the current renderer type 50 */ 51 protected this(HipResourceUsage usage) 52 { 53 import hip.api.renderer.core; 54 super("HipTexture"); 55 _typeID = assetTypeID!HipTexture; 56 textureImpl = HipRenderer.getTextureImplementation(usage); 57 } 58 59 this(const IImage image, HipResourceUsage usage = HipResourceUsage.Immutable) 60 { 61 this(usage); 62 this.img = cast()image; 63 if(image !is null) 64 textureImpl.load(image); 65 } 66 67 Rect getBounds(){return Rect(0,0,getWidth,getHeight);} 68 69 import hip.util.string; 70 SmallString toHipString() 71 { 72 return SmallString("TEX[", getWidth, "x",getHeight,"] ", img.getSizeBytes, " bytes"); 73 } 74 override void onFinishLoading(){} 75 override void onDispose(){} 76 77 override bool isReady() const {return textureImpl !is null;} 78 79 alias textureImpl this; 80 } 81 82 83 class HipTextureRegion : HipAsset, IHipTextureRegion 84 { 85 static immutable float[8] defaultVertices = [0,0, 1,0, 1,1, 0,1]; 86 static immutable Vector2[4] defaultVerticesV = [Vector2(0,0), Vector2(1,0), Vector2(1,1), Vector2(0,1)]; 87 IHipTexture texture; 88 public float u1, v1, u2, v2; 89 protected float[8] vertices; 90 protected float[8] verticesTransformed; 91 private bool flippedX, flippedY; 92 int regionWidth, regionHeight; 93 94 bool hasSuccessfullyLoaded(){return texture && texture.hasSuccessfullyLoaded;} 95 96 protected this() 97 { 98 super("TextureRegion"); 99 _typeID = assetTypeID!HipTextureRegion; 100 } 101 102 this(IHipTexture texture, float u1 = 0, float v1 = 0, float u2 = 1, float v2 = 1) 103 { 104 this(); 105 this.texture = texture; 106 setRegion(u1,v1,u2,v2); 107 } 108 this(IHipTexture texture, uint u1, uint v1, uint u2, uint v2) 109 { 110 this(); 111 this.texture = texture; 112 setRegion(texture.getWidth, texture.getHeight, u1, v1, u2, v2); 113 } 114 115 void setTexture(IHipTexture texture){this.texture = texture;} 116 const(IHipTexture) getTexture() const {return cast(const)texture;} 117 IHipTexture getTexture() {return texture;} 118 int getWidth() const {return regionWidth;} 119 int getHeight() const {return regionHeight;} 120 TextureCoordinatesQuad getRegion() const 121 { 122 return TextureCoordinatesQuad(u1, v1, u2, v2); 123 } 124 125 /** 126 * By passing the width and height values, you'll be able to crop useless frames 127 * Default spritesheet method that makes a spritesheet from the entire texture 128 */ 129 public static Array2D!IHipTextureRegion cropSpritesheet( 130 IHipTexture t, 131 uint frameWidth, uint frameHeight, 132 uint width = 0, uint height = 0, 133 uint offsetX = 0, uint offsetY = 0, 134 uint offsetXPerFrame = 0, uint offsetYPerFrame = 0) 135 { 136 if(width == 0) width = t.getWidth; 137 if(height == 0) height = t.getHeight; 138 139 uint lengthW = width/(frameWidth+offsetXPerFrame); 140 uint lengthH = height/(frameHeight+offsetYPerFrame); 141 142 Array2D!IHipTextureRegion ret = Array2D!IHipTextureRegion(lengthH, lengthW); 143 144 for(int i = 0, fh = 0; fh < height; i++, fh+= frameHeight+offsetXPerFrame) 145 for(int j = 0, fw = 0; fw < width; j++, fw+= frameWidth+offsetYPerFrame) 146 ret[i,j] = new HipTextureRegion(t, offsetX+fw , offsetY+fh, offsetX+fw+frameWidth, offsetY+fh+frameHeight); 147 148 return ret; 149 } 150 public static Array2D!IHipTextureRegion cropSpritesheetRowsAndColumns(IHipTexture t, uint rows, uint columns) 151 { 152 uint frameWidth = t.getWidth() / columns; 153 uint frameHeight = t.getHeight() / rows; 154 return cropSpritesheet(t,frameWidth,frameHeight, t.getWidth, t.getHeight, 0, 0, 0, 0); 155 } 156 157 158 alias setRegion = IHipTextureRegion.setRegion; 159 /** 160 * Defines a region for the texture in the following order: 161 * Top-left 162 * Top-Right 163 * Bot-Right 164 * Bot-Left 165 */ 166 public void setRegion(float u1, float v1, float u2, float v2) 167 { 168 this.u1 = u1; 169 this.u2 = u2; 170 this.v1 = v1; 171 this.v2 = v2; 172 //Check for round 173 float regWidth = (u2 - u1) * texture.getWidth; 174 float regHeight = (v2 - v1) * texture.getHeight; 175 regionWidth = cast(uint)(regWidth + 0.5) > cast(uint)regWidth ? cast(uint)(regWidth+0.5) : cast(uint)regWidth; 176 regionHeight = cast(uint)(regHeight + 0.5) > cast(uint)regHeight ? cast(uint)(regHeight+0.5) : cast(uint)regHeight; 177 178 //Top left 179 vertices[0] = u1; 180 vertices[1] = v1; 181 182 //Top right 183 vertices[2] = u2; 184 vertices[3] = v1; 185 186 //Bot right 187 vertices[4] = u2; 188 vertices[5] = v2; 189 190 //Bot left 191 vertices[6] = u1; 192 vertices[7] = v2; 193 194 if(flippedX) 195 { 196 flippedX = false; 197 setFlippedX(true); 198 } 199 if(flippedY) 200 { 201 flippedY = false; 202 setFlippedY(true); 203 } 204 } 205 206 HipTextureRegion clone() 207 { 208 return new HipTextureRegion(texture, u1, v1, u2, v2); 209 } 210 void setFlippedX(bool flip) 211 { 212 if(flip != flippedX) 213 { 214 flippedX = flip; 215 vertices[0] = flip ? u2 : u1; 216 vertices[2] = flip ? u1 : u2; 217 vertices[4] = flip ? u1 : u2; 218 vertices[6] = flip ? u2 : u1; 219 } 220 } 221 void setFlippedY(bool flip) 222 { 223 if(flip != flippedY) 224 { 225 flippedY = flip; 226 vertices[1] = flip ? v2 : v1; 227 vertices[3] = flip ? v2 : v1; 228 vertices[5] = flip ? v1 : v2; 229 vertices[7] = flip ? v1 : v2; 230 } 231 } 232 bool isFlippedX(){return flippedX;} 233 bool isFlippedY(){return flippedY;} 234 235 ref float[8] getVertices() 236 { 237 return vertices; 238 } 239 override void onFinishLoading(){} 240 override void onDispose(){} 241 override bool isReady() const {return texture !is null;} 242 243 }